package cn.snmp.mutual.uk.stack;

import java.util.*;
import java.io.*;
import java.net.*;
import cn.snmp.mutual.uk.event.*;
import cn.snmp.mutual.uk.net.ContextSocketFace;
import cn.snmp.mutual.uk.net.StreamPortItem;
import cn.snmp.mutual.uk.util.SnmpUtilities;

public abstract class AbstractSnmpContext implements SnmpContextBasisFace, Runnable, RawPduListener {
    private static final String version_id = "@(#)$Id: AbstractSnmpContext.java,v 3.33 2009/03/05 12:48:04 birgita Exp $ Copyright Westhawk Ltd";
    private ContextSocketFace soc;
    private Transmitter[] transmitters;
    private Pdu[] pdus;
    private Thread me;
    private String basename;
    private volatile boolean stopRequested;
    protected String typeSocket;
    protected String hostname;
    protected String bindAddr;
    protected int hostPort;
    protected int maxRecvSize;
    protected boolean isDestroyed;
    protected boolean anyPduExpectingResponse;
    protected RequestPduReceivedSupport pduSupport;
    protected TrapReceivedSupport trapSupport;

    protected abstract void processIncomingResponse(final ByteArrayInputStream p0) throws DecodingException, IOException;

    public abstract byte[] encodePacket(final byte p0, final int p1, final int p2, final int p3, final Enumeration p4, final Object p5) throws IOException, EncodingException;

    public abstract Pdu processIncomingPdu(final byte[] p0) throws DecodingException, IOException;

    public abstract int getVersion();

    protected AbstractSnmpContext(final String host, final int port) throws IOException {
        this(host, port, null, "Standard");
    }

    protected AbstractSnmpContext(final String host, final int port, final String typeSocketA) throws IOException {
        this(host, port, null, typeSocketA);
    }

    protected AbstractSnmpContext(final String host, final int port, final String bindAddress, final String typeSocketA) throws IOException {
        this.anyPduExpectingResponse = false;
        this.pdus = new Pdu[20];
        this.hostname = host;
        this.hostPort = port;
        this.bindAddr = bindAddress;
        this.typeSocket = typeSocketA;
        this.transmitters = new Transmitter[20];
        if (bindAddress != null && !bindAddress.equals("")) {
            this.basename = "SNMP_Stack_Thread_" + host + "_" + port + "_" + bindAddress;
        } else {
            this.basename = "SNMP_Stack_Thread_" + host + "_" + port + "_" + "bindAddressIsNull";
        }
        this.trapSupport = new TrapReceivedSupport(this);
        this.pduSupport = new RequestPduReceivedSupport(this);
        this.isDestroyed = false;
        this.stopRequested = false;
        this.maxRecvSize = 1472;
        this.soc = getSocket(this.typeSocket);
        if (this.soc != null) {
            this.soc.create(this.hostname, this.hostPort, this.bindAddr);
            if (AsnObject.debug > 12) {
                System.out.println(this.getClass().getName() + ": soc.getLocalSocketAddress() = " + this.soc.getLocalSocketAddress());
                System.out.println(this.getClass().getName() + ": soc.getRemoteSocketAddress() = " + this.soc.getRemoteSocketAddress());
            }
        }
    }

    static ContextSocketFace getSocket(final String type) throws IOException {
        ContextSocketFace sf = null;
        if (type != null) {
            String className = null;
            if (type.equals("Standard")) {
                className = "cn.snmp.mutual.uk.co.westhawk.mutual.net.StandardSocket";
            } else if (type.equals("TCP")) {
                className = "TCPSocket";
            } else {
                className = type;
            }
            try {
                final Class cl = Class.forName(className);
                final Object obj = cl.newInstance();
                sf = (ContextSocketFace) obj;
            } catch (ClassNotFoundException exc) {
                final String str = "AbstractSnmpContext.getSocket(): ClassNotFound problem " + exc.getMessage() + ", type=" + type;
                throw new IOException(str);
            } catch (InstantiationException exc2) {
                final String str = "AbstractSnmpContext.getSocket(): Instantiation problem " + exc2.getMessage() + ", type=" + type;
                throw new IOException(str);
            } catch (IllegalAccessException exc3) {
                final String str = "AbstractSnmpContext.getSocket(): IllegalAccess problem " + exc3.getMessage() + ", type=" + type;
                throw new IOException(str);
            } catch (ClassCastException exc4) {
                final String str = "AbstractSnmpContext.getSocket(): ClassCast problem " + exc4.getMessage() + ", type=" + type;
                throw new IOException(str);
            }
            if (sf == null) {
                final String str2 = "AbstractSnmpContext.getSocket(): Cannot create socket " + type;
                throw new IOException(str2);
            }
            if (AsnObject.debug > 12) {
                System.out.println("AbstractSnmpContext.getSocket(): New socket " + sf.getClass().getName());
            }
        }
        return sf;
    }

    public String getHost() {
        return this.hostname;
    }

    @Deprecated
    public String getHostAddress() {
        return this.getSendToHostAddress();
    }

    public String getSendToHostAddress() {
        String res = "";
        if (this.soc != null) {
            res = this.soc.getSendToHostAddress();
        }
        return res;
    }

    public String getReceivedFromHostAddress() {
        String res = "";
        if (this.soc != null) {
            res = this.soc.getReceivedFromHostAddress();
        }
        return res;
    }

    public int getPort() {
        return this.hostPort;
    }

    public String getBindAddress() {
        return this.bindAddr;
    }

    public String getTypeSocket() {
        return this.typeSocket;
    }

    public int getMaxRecvSize() {
        return this.maxRecvSize;
    }

    public void setMaxRecvSize(final int no) {
        this.maxRecvSize = no;
    }

    public String getDebugString() {
        final char[] cret = new char[21];
        for (int i = 0; i < 20; ++i) {
            if (this.transmitters[i] != null) {
                if (this.pdus[i] != null) {
                    cret[i] = (char) (65 + this.pdus[i].getReqId() % 26);
                } else {
                    cret[i] = '=';
                }
            } else {
                cret[i] = '-';
            }
        }
        char res = '0';
        if (this.me != null) {
            ++res;
            if (this.me.isAlive()) {
                ++res;
            }
        }
        cret[20] = res;
        return new String(cret);
    }

    public synchronized void destroy() {
        if (!this.isDestroyed) {
            this.stopRequested = true;
            if (AsnObject.debug > 12) {
                System.out.println(this.getClass().getName() + ".destroy(): Closing socket ");
            }
            this.soc.close();
            this.isDestroyed = true;
            if (this.me == null) {
                this.freeTransmitters();
            }
        }
    }

    public boolean isDestroyed() {
        return this.isDestroyed;
    }

    @Deprecated
    public synchronized void stop() {
        this.stopRequested = true;
    }

    public void run() {
        while (!this.stopRequested) {
            final Thread me = this.me;
            Thread.yield();
            try {
                if (this.stopRequested) {
                    break;
                }
                final StreamPortItem item = this.soc.receive(this.maxRecvSize);
                final ByteArrayInputStream in = item.getStream();
                if (AsnObject.debug > 10) {
                    final int nb = in.available();
                    final byte[] bu = new byte[nb];
                    in.read(bu);
                    in.reset();
                    SnmpUtilities.dumpBytes(this.getClass().getName() + ".run(): Received from " + item.getHostAddress() + ", from port " + item.getHostPort() + ": ", bu);
                }
                this.processIncomingResponse(in);
            } catch (IOException exc) {
                if (exc instanceof InterruptedIOException) {
                    if (AsnObject.debug <= 15) {
                        continue;
                    }
                    System.out.println(this.getClass().getName() + ".run(): Idle recv " + exc.getMessage());
                } else if (exc instanceof SocketException) {
                    if (AsnObject.debug <= 15) {
                        continue;
                    }
                    System.out.println(this.getClass().getName() + ".run(): SocketException " + exc.getMessage());
                } else {
                    if (AsnObject.debug <= 0) {
                        continue;
                    }
                    System.out.println(this.getClass().getName() + ".run(): " + exc.getClass().getName() + " " + exc.getMessage());
                    exc.printStackTrace();
                }
            } catch (DecodingException exc2) {
                if (AsnObject.debug <= 1) {
                    continue;
                }
                System.out.println(this.getClass().getName() + ".run(): DecodingException: " + exc2.getMessage());
            } catch (Exception exc3) {
                if (AsnObject.debug <= 1) {
                    continue;
                }
                System.out.println(this.getClass().getName() + ".run(): Exception: " + exc3.getMessage());
                exc3.printStackTrace();
            } catch (Error err) {
                if (AsnObject.debug <= 1) {
                    continue;
                }
                System.out.println(this.getClass().getName() + ".run(): Error: " + err.getMessage());
                err.printStackTrace();
            }
        }
        this.freeTransmitters();
        this.trapSupport.empty();
        this.pduSupport.empty();
        final ListeningContextPool lcontext = new ListeningContextPool(162, this.bindAddr, this.typeSocket);
        lcontext.removeRawPduListenerFromPool(this);
        this.me = null;
        this.soc = null;
    }

    public synchronized void sendPacket(final byte[] p) {
        if (!this.isDestroyed) {
            this.activate();
            try {
                if (AsnObject.debug > 10) {
                    SnmpUtilities.dumpBytes("Sending to " + this.soc.getSendToHostAddress() + ": ", p);
                }
                this.soc.send(p);
            } catch (IOException exc) {
                if (AsnObject.debug > 0) {
                    System.out.println(this.getClass().getName() + ".sendPacket(): " + exc.getClass().getName() + " " + exc.getMessage());
                    exc.printStackTrace();
                }
            }
        }
    }

    Pdu getPdu(final Integer ReqId) {
        return this.getPdu((int) ReqId);
    }

    Pdu getPdu(final int rid) {
        Pdu ret = null;
        for (int i = 0; i < 20; ++i) {
            if (this.pdus[i] != null && this.pdus[i].getReqId() == rid) {
                ret = this.pdus[i];
                break;
            }
        }
        return ret;
    }

    public synchronized boolean removePdu(final int rid) {
        boolean ret = false;
        for (int i = 0; i < 20; ++i) {
            if (this.pdus[i] != null && this.pdus[i].getReqId() == rid) {
                this.pdus[i] = null;
                ret = true;
                break;
            }
        }
        return ret;
    }

    public synchronized boolean addPdu(final Pdu p) throws IOException, PduException {
        boolean done = false;
        if (this.isDestroyed) {
            throw new EncodingException("Context can no longer be used, since it is already destroyed");
        }
        if (!this.anyPduExpectingResponse) {
            this.anyPduExpectingResponse = p.isExpectingResponse();
        }
        for (int i = 0; i < 20; ++i) {
            if (this.pdus[i] == null) {
                (this.pdus[i] = p).setTrans(this.getTrans(i));
                done = true;
                break;
            }
        }
        return done;
    }

    public void addTrapListener(final TrapListener l) throws IOException {
        this.addTrapListener(l, 162);
    }

    public void removeTrapListener(final TrapListener l) throws IOException {
        this.removeTrapListener(l, 162);
    }

    public void addTrapListener(final TrapListener l, final int port) throws IOException {
        final ListeningContextPool lcontext = new ListeningContextPool(port, this.bindAddr, this.typeSocket);
        this.addTrapListener(l, lcontext);
    }

    public void removeTrapListener(final TrapListener l, final int port) throws IOException {
        final ListeningContextPool lcontext = new ListeningContextPool(port, this.bindAddr, this.typeSocket);
        this.removeTrapListener(l, lcontext);
    }

    public void addTrapListener(final TrapListener l, final ListeningContextPool lcontext) throws IOException {
        this.trapSupport.addTrapListener(l);
        lcontext.addRawPduListener(this);
    }

    public void removeTrapListener(final TrapListener l, final ListeningContextPool lcontext) throws IOException {
        this.trapSupport.removeTrapListener(l);
        if (this.trapSupport.getListenerCount() == 0 && this.pduSupport.getListenerCount() == 0) {
            lcontext.removeRawPduListener(this);
        }
    }

    public void addRequestPduListener(final RequestPduListener l) throws IOException {
        this.addRequestPduListener(l, 161);
    }

    public void removeRequestPduListener(final RequestPduListener l) throws IOException {
        this.removeRequestPduListener(l, 161);
    }

    public void addRequestPduListener(final RequestPduListener l, final int port) throws IOException {
        final ListeningContextPool lcontext = new ListeningContextPool(port, this.bindAddr, this.typeSocket);
        this.addRequestPduListener(l, lcontext);
    }

    public void removeRequestPduListener(final RequestPduListener l, final int port) throws IOException {
        final ListeningContextPool lcontext = new ListeningContextPool(port, this.bindAddr, this.typeSocket);
        this.removeRequestPduListener(l, lcontext);
    }

    public void addRequestPduListener(final RequestPduListener l, final ListeningContextPool lcontext) throws IOException {
        this.pduSupport.addRequestPduListener(l);
        lcontext.addRawPduListener(this);
    }

    public void removeRequestPduListener(final RequestPduListener l, final ListeningContextPool lcontext) throws IOException {
        this.pduSupport.removeRequestPduListener(l);
        if (this.trapSupport.getListenerCount() == 0 && this.pduSupport.getListenerCount() == 0) {
            lcontext.removeRawPduListener(this);
        }
    }

    public void rawPduReceived(final RawPduEvent evt) {
        String hostAddress = evt.getHostAddress();
        final int version = evt.getVersion();
        if (version == this.getVersion()) {
            hostAddress = "127.0.0.1";
            final byte[] message = evt.getMessage();
            Pdu pdu = null;
            try {
                pdu = this.processIncomingPdu(message);
                if (pdu != null) {
                    evt.consume();
                    final int port = evt.getHostPort();
                    if (pdu.getMsgType() == -92 || pdu.getMsgType() == -89) {
                        this.trapSupport.fireTrapReceived(pdu, port, evt);
                    } else {
                        this.pduSupport.fireRequestPduReceived(pdu, port);
                    }
                }
            } catch (DecodingException exc) {
                if (AsnObject.debug > 2) {
                    System.out.println(this.getClass().getName() + ".rawPduReceived(): DecodingException: " + exc.getMessage());
                }
            } catch (IOException exc2) {
                if (AsnObject.debug > 0) {
                    System.out.println(this.getClass().getName() + ".rawPduReceived(): IOException " + exc2.getMessage());
                }
            }
        } else if (AsnObject.debug > 5) {
            final String theirs = SnmpUtilities.getSnmpVersionString(version);
            final String ours = SnmpUtilities.getSnmpVersionString(this.getVersion());
            System.out.println(this.getClass().getName() + ".rawPduReceived(): " + "Pdu version " + theirs + ", does not correspond with context version " + ours);
        }
    }

    Transmitter getTrans(final int i) {
        if (this.transmitters[i] == null) {
            this.transmitters[i] = new Transmitter(this.basename + "_v" + this.getVersion() + "_Trans" + i);
        }
        return this.transmitters[i];
    }

    protected void activate() {
        if (this.me == null && this.anyPduExpectingResponse) {
            this.me = new Thread(this, this.basename + "_v" + this.getVersion() + "_Receive");
            final Thread me = this.me;
            final Thread me2 = this.me;
            me.setPriority(10);
            this.me.start();
        }
    }

    protected void freeTransmitters() {
        for (int i = 0; i < 20; ++i) {
            if (this.transmitters[i] != null) {
                this.transmitters[i].destroy();
                this.transmitters[i] = null;
            }
            if (this.pdus[i] != null) {
                this.pdus[i] = null;
            }
        }
    }

    public abstract Object clone() throws CloneNotSupportedException;

    public abstract String getHashKey();
}
